﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Text;

using Fuse8.DomainFramework.Common;

using Fuse8.ContentManagementFramework.Domain;

namespace Fuse8.ContentManagementFramework.Web.MvcPowered
{
    public static class CmsGridExtensions
    {
        public static CmsGrid<T> CmsGrid<T>(this HtmlHelper htmlHelper, IEnumerable<T> model,
                                            object htmlAttributes = null)
        {
            Guard.ArgumentNotNull(htmlHelper, "htmlHelper");

            return new CmsGrid<T>(htmlHelper, model).Attributes(htmlAttributes);
        }
    }

    public class CmsGrid<T> : CmsMvcBaseComponent<CmsGrid<T>, IEnumerable<T>>
    {
        private bool _renderHeader = true;
        private bool _renderFooter = true;
        private Func<object> _noItemsTemplate;
        private Func<T, string> _rowCssSelector;
        private Func<T, object> _alternativeRowTemplate;

        public IList<CmsGridColumn> GridColumns { get; internal set; }

        public IList<CmsGridRow<T>> GridRows { get; internal set; }

        public string HeaderClass { get; internal set; }

        public CmsGrid(HtmlHelper htmlHelper, IEnumerable<T> model)
            : base(htmlHelper)
        {
            Value = model;

            GridColumns = new List<CmsGridColumn>();

            GridRows = new List<CmsGridRow<T>>();

            InlineTemplate = (p) => { return p; }; //just stub

            Component = this;
        }

        public CmsGrid<T> Columns(Action<CmsGridColumnBuilder<T>> configurator)
        {
            Guard.ArgumentNotNull(configurator, "configurator");

            var builder = new CmsGridColumnBuilder<T> {Container = this};

            configurator(builder);

            return this;
        }

        public CmsGrid<T> Rows(Action<CmsGridRowBuilder<T>> configurator)
        {
            Guard.ArgumentNotNull(configurator, "configurator");

            var builder = new CmsGridRowBuilder<T> {Container = this};

            configurator(builder);

            return this;
        }

        public CmsGrid<T> AlternativeRow(Func<T, object> template)
        {
            _alternativeRowTemplate = template;
            return this;
        }

        public CmsGrid<T> RenderHeader(bool needRender)
        {
            _renderHeader = needRender;

            return this;
        }

        public CmsGrid<T> RenderFooter(bool needRender)
        {
            _renderFooter = needRender;

            return this;
        }

        public CmsGrid<T> NoItemsTemplate(Func<object> template)
        {
            Guard.ArgumentNotNull(template, "template");

            _noItemsTemplate = template;

            return this;
        }

        public CmsGrid<T> RowCssCondition(Func<T, string> condition)
        {
            Guard.ArgumentNotNull(condition, "condition");

            _rowCssSelector = condition;

            return this;
        }

        protected override string RenderComponent()
        {
            string result = string.Empty;

            if (_renderHeader)
            {
                result = base.RenderComponent();
            }
            else
            {
                var sb = new StringBuilder();

                RenderBodyTemplate(sb);

                result = sb.ToString();
            }

            return result;
        }

        protected override string RenderTemplate()
        {
            string result = string.Empty;

            if ((Value != null) && (Value.Any()))
            {
                var tableBuilder = new TagBuilder("table");

                StringBuilder sb = new StringBuilder();

                if (GridColumns.Any())
                {
                    if (_renderHeader)
                    {
                        RenderHeaderTemplate(sb);
                    }
                }

                sb.AppendLine(new TagBuilder("tbody").ToString(TagRenderMode.StartTag));

                RenderBodyTemplate(sb);

                sb.AppendLine(new TagBuilder("tbody").ToString(TagRenderMode.EndTag));

                tableBuilder.InnerHtml = sb.ToString();

                result = tableBuilder.ToString(TagRenderMode.Normal);
            }
            else if (_noItemsTemplate != null)
            {
                result = _noItemsTemplate().ToString();
            }

            return result;
        }

        protected virtual void RenderHeaderTemplate(StringBuilder sb)
        {
            sb.AppendLine(new TagBuilder("colgroup").ToString(TagRenderMode.StartTag));

            TagBuilder columnBuilder;
            TagBuilder headerItemBuilder;
            StringBuilder sbHeader = new StringBuilder();
            foreach (var column in GridColumns)
            {
                columnBuilder = new TagBuilder("col");

                if (column.PixelWidth > 0)
                {
                    columnBuilder.MergeAttribute("style", string.Format("width:{0}px", column.PixelWidth));
                }

                headerItemBuilder = new TagBuilder("th");
                headerItemBuilder.MergeAttribute("scope", "col");

                if (column.HtmlAttributes != null)
                {
                    headerItemBuilder.MergeAttributes(
                        HtmlHelper.AnonymousObjectToHtmlAttributes(column.HtmlAttributes), true);
                }
                headerItemBuilder.InnerHtml = string.Format("<span>{0}</span>", column.Title);

                sbHeader.AppendLine(headerItemBuilder.ToString(TagRenderMode.Normal));

                sb.AppendLine(columnBuilder.ToString(TagRenderMode.Normal));
            }

            sb.AppendLine(new TagBuilder("colgroup").ToString(TagRenderMode.EndTag));

            var headerBuilder = new TagBuilder("thead");
            if (!string.IsNullOrWhiteSpace(HeaderClass))
            {
                headerBuilder.AddCssClass(HeaderClass);
            }
            sb.AppendLine(headerBuilder.ToString(TagRenderMode.StartTag));
            sb.AppendLine(new TagBuilder("tr").ToString(TagRenderMode.StartTag));

            sb.AppendLine(sbHeader.ToString());

            sb.AppendLine(new TagBuilder("tr").ToString(TagRenderMode.EndTag));
            sb.AppendLine(new TagBuilder("thead").ToString(TagRenderMode.EndTag));
        }

        protected virtual void RenderBodyTemplate(StringBuilder sb)
        {
            TagBuilder rowBuilder;
            TagBuilder trBuilder;
            foreach (var item in Value)
            {
                trBuilder = new TagBuilder("tr");

                if (_rowCssSelector != null)
                {
                    trBuilder.AddCssClass(_rowCssSelector(item));
                }

                sb.AppendLine(trBuilder.ToString(TagRenderMode.StartTag));

                foreach (var row in GridRows)
                {
                    rowBuilder = new TagBuilder("td");

                    rowBuilder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(row.HtmlAttributes), true);

                    rowBuilder.InnerHtml = row.RowItemTemplate(item).ToString();

                    sb.AppendLine(rowBuilder.ToString(TagRenderMode.Normal));
                }

                sb.AppendLine(new TagBuilder("tr").ToString(TagRenderMode.EndTag));

                if (_alternativeRowTemplate != null)
                {
                    sb.AppendLine(_alternativeRowTemplate(item).ToString());
                }
            }
        }
    }

    #region Columns

    public class CmsGridColumn
    {
        public string Title { get; set; }

        public int PixelWidth { get; set; }

        public object HtmlAttributes { get; set; }
    }

    public class CmsGridColumnBuilder<T>
    {
        public CmsGrid<T> Container { get; internal set; }

        public CmsGridColumnItemBuilder Bound()
        {
            var column = new CmsGridColumn();

            Container.GridColumns.Add(column);

            var builder = new CmsGridColumnItemBuilder(column);

            return builder;
        }

        public CmsGridColumnBuilder<T> HeaderCssClass(string cssClass)
        {
            Guard.ArgumentNotEmpty(cssClass, "cssClass");

            Container.HeaderClass = cssClass;

            return this;
        }
    }

    public class CmsGridColumnItemBuilder
    {
        private CmsGridColumn _column;

        public CmsGridColumnItemBuilder(CmsGridColumn column)
        {
            Guard.ArgumentNotNull(column, "column");

            _column = column;
        }

        public CmsGridColumnItemBuilder Title(string title)
        {
            _column.Title = title;

            return this;
        }

        public CmsGridColumnItemBuilder Width(int pixelWidth)
        {
            _column.PixelWidth = pixelWidth;

            return this;
        }

        public CmsGridColumnItemBuilder HtmlAttributes(object htmlAttributes)
        {
            _column.HtmlAttributes = htmlAttributes;

            return this;
        }
    }

    #endregion

    #region Rows

    public class CmsGridRow<T>
    {
        public Func<T, object> RowItemTemplate { get; set; }

        public int Width { get; set; }

        public object HtmlAttributes { get; set; }
    }

    public class CmsGridRowBuilder<T>
    {
        public CmsGrid<T> Container { get; internal set; }

        public CmsGridRowItemBuilder<T> Bound()
        {
            var row = new CmsGridRow<T>();

            Container.GridRows.Add(row);

            var builder = new CmsGridRowItemBuilder<T>(row);

            return builder;
        }
    }

    public class CmsGridRowItemBuilder<T>
    {
        private CmsGridRow<T> _row;

        public CmsGridRowItemBuilder(CmsGridRow<T> row)
        {
            Guard.ArgumentNotNull(row, "row");

            _row = row;
        }

        public CmsGridRowItemBuilder<T> RowItemTemplate(Func<T, object> inlineTemplate)
        {
            _row.RowItemTemplate = inlineTemplate;

            return this;
        }

        public CmsGridRowItemBuilder<T> Width(int width)
        {
            _row.Width = width;

            return this;
        }

        public CmsGridRowItemBuilder<T> HtmlAttributes(object htmlAttributes)
        {
            _row.HtmlAttributes = htmlAttributes;

            return this;
        }
    }

    #endregion
}
